package omg.SongHandling.DataHandling;

import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;
import java.util.logging.Logger;
import omg.Error.ConverterCreationFailedError;
import omg.Error.NoConnectionError;
import omg.Error.NoValidConverterError;

public class ConverterManager {

        private static ConverterManager instance = null;
        private static String converterPackage = "omg.SongHandling.DataHandling";
	private static String[] registeredConverters = {"ConverterGeneric"};

        /* SINGLETON PATTERN */
        private ConverterManager() {}
	public static ConverterManager getInstance() {
            if (null == ConverterManager.instance)
                ConverterManager.instance = new ConverterManager();
            return ConverterManager.instance;
	}

	/**
	 * Iterates through all Converters known to the class and returns the first
         * one feeling responsible for the supplied Stream.
         * @param stream the stream to find a responsible converter for
         * @return the suitable Converter
         * @throws NoValidConverterError if no fitting Converter could be found
         * @throws NoConnectionError if no connection to the stream target could be established
	 */
	public Converter getConverter(Stream stream) throws NoValidConverterError, NoConnectionError {
            for (String converter : ConverterManager.registeredConverters) {
                try {
                    // get class to use dynamic method call and object creation
                    Class cl = Class.forName(converterPackage + '.' + converter);
                    if ((Boolean) cl.getMethod("isResponsibleFor", Stream.class).invoke(null, stream))
                        return (Converter) cl.getConstructor(Stream.class).newInstance(stream);
                } catch (IllegalAccessException ex) {
                    Logger.getLogger("Periphery").log(Level.WARNING, converter + " constructor(Stream) or isResponsibleFor(Stream) is declared private/protected", ex);
                } catch (IllegalArgumentException ex) {
                    Logger.getLogger("Periphery").log(Level.WARNING, converter + " constructor(Stream) or isResponsibleFor(Stream) requires wrong arguments", ex);
                } catch (InvocationTargetException ex) {
                    if (NoConnectionError.class == ex.getCause().getClass())
                        throw (NoConnectionError)ex.getCause();
                    else if (ConverterCreationFailedError.class == ex.getCause().getClass())
                        Logger.getLogger("Periphery").log(Level.WARNING, converter + " constructor(Stream) caused an controlled exception", ex.getCause());
                    else
                        Logger.getLogger("Periphery").log(Level.WARNING, converter + " constructor(Stream) caused an unexpected exception", ex.getCause());
                } catch (NoSuchMethodException ex) {
                    Logger.getLogger("Periphery").log(Level.WARNING, converter + " constructor(Stream) or isResponsibleFor(Stream) doesn't exist");
                } catch (ClassNotFoundException ex) {
                    Logger.getLogger("Periphery").log(Level.WARNING, converter + " is registered as known converter, but the class couldn't be found");
                } catch (Exception ex) {
                    Logger.getLogger("Periphery").log(Level.WARNING, converter + " contains some not nearer specified error",  ex);
                }
            }
            throw new NoValidConverterError();
	}

}